﻿using System;
using System.Net;
using System.Text;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;

namespace eventModel
{
    /// <summary>Represents a cached executable compilation.</summary>
    public class CachedCompilation
    {
        public int Executions { get; set; } // Counts execution of cached reference.
        public object ObjectHandle { get; set; } // Handle to object instance
        public MethodInfo MethodHandle { get; set; } // Handle to method
        public DateTime LastCompilation { get; set; } // Specifies the last time the hash was generated.
    }

    public class Pipeline
    {
        // Requests served:
        public static int RequestsServed { get; set; }
        public static Int64 TimeTaken { get; set; }
        public static string ServerDir { get; set; } 
        public static bool EnableLogging { get; set; }
        public static bool AllowExecutables { get; set; }

        public static int MaxBinaryCacheSize { get; set; }
        public static int MaxTextFileCacheSize { get; set; }

        //Keeps an in-memory hash table reference cache of binary and text streams: 
        private static Dictionary<string, byte[]> BinaryCache { get; set; }
        private static Dictionary<string, string> TextCache { get; set; }

        static Pipeline()
        {
            MaxBinaryCacheSize = 25;
            MaxTextFileCacheSize = 100;
            BinaryCache = new Dictionary<string, byte[]>();
            TextCache = new Dictionary<string, string>();
        }

        /// <summary>Cleans up the binary stream cache if we've reached our max</summary>
        public static void CleanBinaryStreamCache(string parameter)
        {
            // Dave: This would REALLY benefit from the memory management factory... 
            // these object would get destroyed and recreated very often.
            if (BinaryCache.Count > (MaxBinaryCacheSize - 1))
            {
                Logging.Add("SERVER - Max allowed cached binary streams, cleaning up.");
                //TODO! Still gotta do this... don't worry, nothing's being added to cache yet.
            }
        }

        /// <summary>Cleans up the text stream cache if we've reached our max</summary>
        public static void CleanTextStreamCache(string parameter)
        {
            if (TextCache.Count > (MaxTextFileCacheSize - 1))
            {
                Logging.Add("SERVER - Max allowed cached binary streams, cleaning up.");
                //TODO! Still gotta do this... don't worry, nothing's being added to cache yet.
            }
        }


        // Handle our incoming connections:
        public static void HandleConnection(object cntxt)
        {
            // This is for benchmarking... time our request:
            System.Diagnostics.Stopwatch stp = new System.Diagnostics.Stopwatch();
            if (EnableLogging) { stp.Start(); }

            // Get our context: 
            HttpListenerContext context = (HttpListenerContext)cntxt;
            Context prmCntxt = new Context();
            prmCntxt.Request = context.Request;
            prmCntxt.Response = context.Response;

            // Generate a standard response:
            string standardResponse = "<html><title>501 ERROR</title><body><h1>501</h1>This request is not supported.<body></html>";
            prmCntxt.Response.KeepAlive = false;

            byte[] buffer = null;

            //TODO: This switch is turning ugly.... we need to rewrite this...
            switch (prmCntxt.Request.HttpMethod.ToUpper())
            {
                case "GET":
                    {
                        // Get the request's parts:
                        string[] RestParams = prmCntxt.Request.RawUrl.Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries);
                        string fil = RestParams[0].Substring(1);

                        if (RestParams.Length > 0)
                        {
                            if (File.Exists(fil)) // if existing file:
                            {
                                string ext = fil.Substring(fil.Length - 3, 3).ToLower();
                                // if we can compile it:
                                if (AllowExecutables && ext == ".cs")
                                { 
                                    standardResponse = Compiler.Compile(fil, prmCntxt);
                                    buffer = System.Text.Encoding.UTF8.GetBytes(standardResponse);
                                }
                                else 
                                {
                                    if (ext == ".txt" || ext == ".htm" || ext == ".html")
                                    { 
                                        standardResponse = File.ReadAllText(fil);
                                        buffer = System.Text.Encoding.UTF8.GetBytes(standardResponse);
                                    }
                                    else  { buffer = File.ReadAllBytes(fil); }
                                    
                                } // otherwise return text...
                            }
                            // file doesnt exist, buddy:
                            else 
                            { 
                                standardResponse = "<html><title>404 ERROR</title><body><h1>404</h1>The specified file does not exist<body></html>";
                                buffer = System.Text.Encoding.UTF8.GetBytes(standardResponse);
                            }
                        }
                        // Don't support default pages yet...
                        else 
                        { 
                            standardResponse = "<html><title>501 ERROR</title><body><h1>501</h1>No default file specified.<body></html>";
                            buffer = System.Text.Encoding.UTF8.GetBytes(standardResponse);
                        }

                        break;
                    }
            }

            if (EnableLogging)
            {
                // Stop the stop watch benchmark:
                stp.Stop();
                TimeTaken += stp.ElapsedMilliseconds;
                Logging.Add("<i>Request verb " + prmCntxt.Request.HttpMethod.ToUpper() + " for " + prmCntxt.Request.RawUrl + "</i>; Served in " + stp.ElapsedMilliseconds + " milliseconds. (" + stp.ElapsedTicks + " ticks)");
            }

            System.IO.Stream output = prmCntxt.Response.OutputStream;
            if (prmCntxt.Response.OutputStream.CanWrite)
            {

                
                prmCntxt.Response.ContentLength64 = buffer.Length;

                // Send back results and close up the connections:
                output.Write(buffer, 0, buffer.Length);
                output.Close();
                prmCntxt.Response.Close();
            }
            else
            {
                try
                {
                    output.Close();
                    prmCntxt.Response.Close();
                }
                catch {  }
            }

            // Keep count of requests served:
            RequestsServed++;
        }
    }
}
